{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95689a63",
   "metadata": {},
   "outputs": [],
   "source": [
    "from openai import OpenAI\n",
    "from dotenv import load_dotenv\n",
    "from IPython.display import display, Markdown, update_display\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0fee3ac3",
   "metadata": {},
   "outputs": [],
   "source": [
    "load_dotenv(override=True)\n",
    "gpt = OpenAI()\n",
    "llama = OpenAI(\n",
    "    api_key=\"ollama\",\n",
    "    base_url=\"http://localhost:11434/v1\"\n",
    ")\n",
    "gpt_model = \"gpt-4o-mini\"\n",
    "llama_model = \"llama3.2\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "309bde84",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81d971f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "class Classroom:\n",
    "\n",
    "    def __init__(self, topic=\"LLM\", display_handle = display(Markdown(\"\"), display_id=True), response = \"\"):\n",
    "        self.display_handle = display_handle\n",
    "        self.response = response\n",
    "\n",
    "        self.tutor_system = f\"You are the tutor who is expert in {topic}. You know best practices in how to impart knowledge on amateur and pro students in very organized way. You first declare the contents of your message separately for amateur and pro student, and then you list down the information in the same order in very organized way such that it's very readable and easy to understand.you highlight the key points every time. you explain with examples, and you have a quite good sense of humor, which you include in your examples and way of tutoring as well. You wait for go ahead from all your students before you move next to the new topic\"\n",
    "\n",
    "        self.amateur_system = f\"You are a student who is here to learn {topic}. You ask very basic questions(which comes to mind of a person who has heard the topic for the very first time) but you are intelligent and don't ask stupid questions. you put your question in very organized way. Once you understand a topic you ask tutor to move forward with new topic\"\n",
    "\n",
    "        self.pro_system = f\"You are expert of {topic}. You cross-question the tutor to dig deeper into the topic, so that nothing inside the topic is left unknown and unmentioned by the tutor. you post your questions in a very organized manner highlighting the keypoints, such that an amateur can also understand your point or query that you are making. You complement the queries made by amateur and dig deeper into the concept ask by him as well. You also analyze the tutor's response such that it doesn't miss anything and suggest improvements in it as well. Once you understand a topic you ask tutor to move forward with new topic\"\n",
    "\n",
    "        self.tutor_messages = [\"Hi, I'm an expert on LLMs!\"]\n",
    "        self.amateur_messages = [\"Hi, I'm new to LLMs. I just heard someone using this term in office.\"]\n",
    "        self.pro_messages = [\"Hey, I'm here to brush up my knowledge on LLMs and gain a more deeper understanding of LLMs\"]\n",
    "        \n",
    "    def call_tutor(self):\n",
    "        messages = [\n",
    "            {\"role\": \"system\", \"content\": self.tutor_system}\n",
    "        ]\n",
    "        for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
    "            messages.append({\"role\": \"assistant\", \"content\": f\"tutor: {tutor}\"})\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {amateur}\"})\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"pro: {pro}\"})\n",
    "\n",
    "        if len(self.amateur_messages) > len(self.tutor_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.amateur_messages[-1]}\"})\n",
    "\n",
    "        if len(self.pro_messages) > len(self.tutor_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.pro_messages[-1]}\"})\n",
    "\n",
    "        stream = llama.chat.completions.create(\n",
    "            model = llama_model,\n",
    "            messages = messages,\n",
    "            stream=True\n",
    "        )\n",
    "        self.response += \"\\n\\n\\n# Tutor: \\n\"\n",
    "        response = \"\"\n",
    "        for chunk in stream:\n",
    "            self.response += chunk.choices[0].delta.content or ''\n",
    "            response += chunk.choices[0].delta.content or ''\n",
    "            update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
    "        \n",
    "        self.tutor_messages.append(response)\n",
    "\n",
    "\n",
    "\n",
    "    def call_amateur(self):\n",
    "        messages = [\n",
    "            {\"role\": \"system\", \"content\": self.amateur_system}\n",
    "        ]\n",
    "        for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"tutor: {tutor}\"})\n",
    "            messages.append({\"role\": \"assistant\", \"content\": f\"amateur: {amateur}\"})\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"pro: {pro}\"})\n",
    "\n",
    "        if len(self.tutor_messages) > len(self.amateur_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.tutor_messages[-1]}\"})\n",
    "\n",
    "        if len(self.pro_messages) > len(self.amateur_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.pro_messages[-1]}\"})\n",
    "\n",
    "        stream = llama.chat.completions.create(\n",
    "            model = llama_model,\n",
    "            messages = messages,\n",
    "            stream=True\n",
    "        )\n",
    "        self.response += \"\\n\\n\\n# Amateur: \\n\"\n",
    "        response = \"\"\n",
    "        for chunk in stream:\n",
    "            self.response += chunk.choices[0].delta.content or ''\n",
    "            response += chunk.choices[0].delta.content or ''\n",
    "            update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
    "        \n",
    "        self.amateur_messages.append(response)\n",
    "\n",
    "\n",
    "\n",
    "    def call_pro(self):\n",
    "        messages = [\n",
    "            {\"role\": \"system\", \"content\": self.pro_system}\n",
    "        ]\n",
    "        for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"tutor: {tutor}\"})\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {amateur}\"})\n",
    "            messages.append({\"role\": \"assistant\", \"content\": f\"pro: {pro}\"})\n",
    "            \n",
    "        if len(self.tutor_messages) > len(self.pro_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.tutor_messages[-1]}\"})\n",
    "\n",
    "        if len(self.amateur_messages) > len(self.pro_messages):\n",
    "            messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.amateur_messages[-1]}\"})\n",
    "\n",
    "        stream = llama.chat.completions.create(\n",
    "            model = llama_model,\n",
    "            messages = messages,\n",
    "            stream=True\n",
    "        )\n",
    "        self.response += \"\\n\\n\\n# Pro: \\n\"\n",
    "        response = \"\"\n",
    "        for chunk in stream:\n",
    "            response = chunk.choices[0].delta.content or ''\n",
    "            self.response +=  response\n",
    "            update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
    "\n",
    "        self.pro_messages.append(response)\n",
    "\n",
    "    def discuss(self, n=5):\n",
    "        for i in range(n):\n",
    "            self.call_tutor()\n",
    "            self.call_amateur()\n",
    "            self.call_pro()\n",
    "cls = Classroom(\"LLM\")\n",
    "cls.discuss()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6406d5ee",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
